#include <Windows.h>
#include <tchar.h>
#include "resource.h"
#include <CommCtrl.h>

#define		WM_INITMONITOR		(WM_APP + 1000)

struct SMonitorInfo {
	HANDLE						hHandle;
	TCHAR						szFilePath[MAX_PATH];
	FILE_NOTIFY_INFORMATION     fni[1024];
	DWORD						dwBufLength;
	OVERLAPPED					Overlapped;
	HANDLE						hCompPort;
	HWND						hLstBox;
};
	//static HANDLE hCompPort;

void RetrieveDriveName(TCHAR* szDriveName, TCHAR* szItem)
{
	if (NULL != szItem && NULL != szDriveName)
	{
		TCHAR* szPos = _tcsstr(szItem, _T(":\\"));
		if (NULL != szPos && szPos > szItem)
		{
			memcpy(szDriveName, --szPos, 3 * sizeof(TCHAR));
			szDriveName[3] = _T('\0');
		}
	}

	return;
}

DWORD CALLBACK ChangeMonitorProc(LPVOID lpData)
{
	DWORD						numBytes;
    LPOVERLAPPED				lpOverlapped;
    PFILE_NOTIFY_INFORMATION	fni;
	SMonitorInfo* pMonitorInfo = (SMonitorInfo*)lpData;

	DWORD dwFilter = FILE_NOTIFY_CHANGE_CREATION|
					 FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME;
					 //|FILE_NOTIFY_CHANGE_LAST_WRITE|FILE_NOTIFY_CHANGE_LAST_ACCESS|
					 //FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_ATTRIBUTES FILE_NOTIFY_CHANGE_SECURITY|
	do
	{
		ReadDirectoryChangesW(pMonitorInfo->hHandle, pMonitorInfo->fni, sizeof(pMonitorInfo->fni), TRUE, 
							  dwFilter, &pMonitorInfo->dwBufLength, &pMonitorInfo->Overlapped, NULL);

		GetQueuedCompletionStatus(pMonitorInfo->hCompPort, &numBytes, (LPDWORD)&pMonitorInfo, &lpOverlapped, INFINITE);

		TCHAR szPrompt[MAX_PATH]	= {0};			
		DWORD nIndex = 0; 	
		if (pMonitorInfo)
		{
			do {
				switch (pMonitorInfo->fni[nIndex].Action)
				{
					case FILE_ACTION_ADDED:					_tcscpy(szPrompt, _T("Added file"));		break;
					case FILE_ACTION_MODIFIED:				_tcscpy(szPrompt, _T("Modify file"));		break;			
					case FILE_ACTION_REMOVED:				_tcscpy(szPrompt, _T("Remove file"));		break;
					case FILE_ACTION_RENAMED_OLD_NAME:		_tcscpy(szPrompt, _T("Rename file"));		break;	
					case FILE_ACTION_RENAMED_NEW_NAME:		_tcscpy(szPrompt, _T("Rename file"));		break;
				}
				pMonitorInfo->fni[nIndex].FileName[pMonitorInfo->fni[nIndex].FileNameLength/2] = NULL;
				
				//GetCurrentDirectory();
				_stprintf(szPrompt, _T("%s: %s\\%s\n"), szPrompt, pMonitorInfo->szFilePath, pMonitorInfo->fni[nIndex].FileName);
				SendMessage(pMonitorInfo->hLstBox, LB_ADDSTRING, 0, (LPARAM)szPrompt);
				nIndex++;
			} while (!pMonitorInfo->fni[nIndex].NextEntryOffset);
		}
	} while(pMonitorInfo);
	
	return 0;
}

int CALLBACK DlgProc(HWND hDlg, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND	 hLstReport	= NULL;
	static HANDLE hThread	= NULL;
	static SMonitorInfo MonitorInfo;


	switch (nMsg)
	{
		case WM_INITDIALOG:
		{
			SetWindowText(hDlg, _T("Demo0066"));
			UINT nWidth		= GetSystemMetrics(SM_CXSCREEN);
			UINT nHeight	= GetSystemMetrics(SM_CYSCREEN);
			RECT rtClient;
			GetClientRect(hDlg, &rtClient);
			SetWindowPos(hDlg, NULL, (nWidth - (rtClient.right - rtClient.left)) / 2, (nHeight - (rtClient.bottom - rtClient.top)) / 2, 0, 0, SWP_NOSIZE);
			
			hLstReport = GetDlgItem(hDlg, IDC_LSTREPORT);

			TCHAR szBuffer[MAX_PATH];
			GetLogicalDriveStrings(MAX_PATH, szBuffer);
			TCHAR* szPos = szBuffer;
			while (_T('\0') != *szPos)
			{
				TCHAR szDriveName[32];
				TCHAR szVolumeBuf[MAX_PATH];
				TCHAR szItemData[MAX_PATH];
				_tcscpy(szDriveName, szPos);
				GetVolumeInformation(szDriveName, szVolumeBuf, MAX_PATH, NULL, NULL, NULL, NULL, 0);
				_stprintf(szItemData, _T("%s - %s"), szDriveName, szVolumeBuf);
				SendDlgItemMessage(hDlg, IDC_LSTDRIVES, LB_ADDSTRING, 0, (LPARAM)szItemData);
				
				szPos += _tcslen(szDriveName);
				szPos ++;
			}

			break;
		}
		case WM_COMMAND:
		{
			switch (LOWORD(wParam))
			{
				case IDC_BTNCLEAR:
				{
					int nCount = SendMessage(hLstReport, LB_GETCOUNT, 0, 0);
					for (int ii = nCount - 1; ii >= 0; ii--)
					{
						SendMessage(hLstReport, LB_DELETESTRING, (WPARAM)ii, 0);
					}
					
					break;
				}				
				case IDC_BTNSTART:
				{
					TCHAR szBtnName[MAX_PATH];
					GetDlgItemText(hDlg, IDC_BTNSTART, szBtnName, MAX_PATH);
					if (0 == _tcsicmp(szBtnName, _T("Start")))
					{
						EnableWindow(GetDlgItem(hDlg, IDC_BTNSTART), FALSE);

						TCHAR szItem[64];
						int nItem = SendDlgItemMessage(hDlg, IDC_LSTDRIVES, LB_GETCURSEL, 0, 0);					
						SendDlgItemMessage(hDlg, IDC_LSTDRIVES, LB_GETTEXT, (WPARAM)nItem, (LPARAM)szItem);
						RetrieveDriveName(MonitorInfo.szFilePath, szItem);
						MonitorInfo.hHandle = CreateFile(MonitorInfo.szFilePath, 
														 FILE_LIST_DIRECTORY, 
														 FILE_SHARE_READ|FILE_SHARE_DELETE, 
														 NULL, 
														 OPEN_EXISTING, 
														 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED,
														 NULL);
						MonitorInfo.hLstBox	  = hLstReport;
						MonitorInfo.hCompPort = CreateIoCompletionPort(MonitorInfo.hHandle, NULL,(DWORD)&MonitorInfo, 0);
						
						// Create a thread to sit on the directory changes
						hThread = CreateThread(NULL, NULL, ChangeMonitorProc, (LPVOID)&MonitorInfo, NULL, NULL);
					}
							
					break;
				}
				case IDC_BTNEXIT:
				{
					PostQueuedCompletionStatus(MonitorInfo.hCompPort, 0, 0, NULL);
					WaitForSingleObject(hThread, INFINITE);
					
					if (NULL != MonitorInfo.hCompPort )
					{
						CloseHandle(MonitorInfo.hCompPort );
					}

					EndDialog(hDlg, 0);
					break;
				}
			}
			break;
		}
		case WM_CLOSE:
		{
			PostQuitMessage(0);
			break;
		}
	}

	return 0;
}

BOOL WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPTSTR szCmdLine, int nShowCmd)
{
	DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG), NULL, DlgProc);

	return TRUE;
}